import dayjs from 'dayjs'
import $ from 'jquery'
import numeral from 'numeral'
import {
  columeHeader_word,
  luckysheetdefaultFont,
} from '../controllers/constant'
import menuButton from '../controllers/menuButton'
import { isdatatype, isdatatypemulti } from '../global/datecontroll'
import { hasChinaword, isRealNum } from '../global/validate'
import locale from '../locale/locale'
import Store from '../store'
// import method from '../global/method';

/**
 * Common tool methods
 */

/**
 * Determine whether a string is in standard JSON format
 * @param {string} str
 */
function isJsonString(str: string) {
  try {
    if (typeof JSON.parse(str) == 'object') {
      return true
    }
  }
  catch (e) {
    console.warn(e)
  }
  return false
}

/**
 * extend two objects
 * @param {object} jsonbject1
 * @param {object} jsonbject2
 */
function common_extend(jsonbject1, jsonbject2) {
  const resultJsonObject = {}

  for (const attr in jsonbject1) {
    resultJsonObject[attr] = jsonbject1[attr]
  }

  for (const attr in jsonbject2) {
    // undefined is equivalent to no setting
    if (jsonbject2[attr] === undefined) {
      continue
    }
    resultJsonObject[attr] = jsonbject2[attr]
  }

  return resultJsonObject
}

// 替换temp中的${xxx}为指定内容 ,temp:字符串，这里指html代码，dataarry：一个对象{"xxx":"替换的内容"}
// 例：luckysheet.replaceHtml("${image}",{"image":"abc","jskdjslf":"abc"})   ==>  abc
function replaceHtml(temp, dataarry) {
  return temp.replace(/\$\{(\w+)\}/g, (s1, s2) => {
    const s = dataarry[s2]
    if (typeof s != 'undefined') {
      return s
    }
    else {
      return s1
    }
  })
}

// 获取数据类型
function getObjType(obj) {
  const toString = Object.prototype.toString

  const map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object',
  }

  // if(obj instanceof Element){
  //     return 'element';
  // }

  return map[toString.call(obj)]
}

// 获取当前日期时间
function getNowDateTime(format: number) {
  const now = dayjs()

  if (format === 2) {
    return now.format('YY-MM-DD HH:mm:ss')
  }
  else {
    return now.format('YYYY-MM-DD')
  }
}

/**
 * 颜色 16进制转rgb
 * @param hex
 */
function hexToRgb(hex: string) {
  const color: string[] = []
  const rgb: number[] = []
  hex = hex.replace(/#/, '')

  if (hex.length === 3) {
    // 处理 "#abc" 成 "#aabbcc"
    const tmp: string[] = []

    for (let i = 0; i < 3; i++) {
      tmp.push(hex.charAt(i) + hex.charAt(i))
    }

    hex = tmp.join('')
  }

  for (let i = 0; i < 3; i++) {
    color[i] = `0x${hex.substr(i + 2, 2)}`
    rgb.push(Number.parseInt(color[i]))
  }

  return `rgb(${rgb.join(',')})`
}

/**
 * 颜色 rgb转16进制
 * @param color
 * @returns String
 */
function rgbTohex(color: string) {
  let rgb: string[]

  if (color.includes('rgba')) {
    rgb = color.replace('rgba(', '').replace(')', '').split(',')
  }
  else {
    rgb = color.replace('rgb(', '').replace(')', '').split(',')
  }

  const r = Number.parseInt(rgb[0])
  const g = Number.parseInt(rgb[1])
  const b = Number.parseInt(rgb[2])

  const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`

  return hex
}

// 列下标  字母转数字
function ABCatNum(a: string) {
  if (a == null || a.length === 0) {
    return Number.NaN
  }
  const str = a.toLowerCase().split('')
  const al = str.length
  const getCharNumber = function (charx) {
    return charx.charCodeAt() - 96
  }
  let numout = 0
  let charnum = 0
  for (let i = 0; i < al; i++) {
    charnum = getCharNumber(str[i])
    numout += charnum * 26 ** (al - i - 1)
  }
  // console.log(a, numout-1);
  if (numout === 0) {
    return Number.NaN
  }
  return numout - 1
}

// 列下标  数字转字母
function chatatABC(n: number) {
  const orda = 'a'.charCodeAt(0)

  const ordz = 'z'.charCodeAt(0)

  const len = ordz - orda + 1

  let s = ''

  while (n >= 0) {
    s = String.fromCharCode((n % len) + orda) + s

    n = Math.floor(n / len) - 1
  }

  return s.toUpperCase()
}

function ceateABC(index) {
  const wordlen = columeHeader_word.length

  if (index < wordlen) {
    return columeHeader_word
  }
  else {
    let relist: string[] = []
    let i = 2
    let n = 0

    while (index < (wordlen / (wordlen - 1)) * (wordlen ** i - 1)) {
      n = i
      i++
    }

    for (let x = 0; x < n; x++) {
      if (x === 0) {
        relist = relist.concat(columeHeader_word)
      }
      else {
        relist = relist.concat(createABCdim(x)!, index)
      }
    }
  }
}

function createABCdim(x?: number) {
  const chwl = columeHeader_word.length

  if (x === 1) {
    const ret: string[] = []
    let c = 0

    for (let i = 0; i < chwl; i++) {
      const b = columeHeader_word[i]

      for (let n = 0; n < chwl; n++) {
        const bq = b + columeHeader_word[n]
        ret.push(bq)
        c++

        if (c > n) {
          return ret
        }
      }
    }
  }
  else if (x === 2) {
    const ret: string[] = []
    let c = 0

    for (let i = 0; i < chwl; i++) {
      const bb = columeHeader_word[i]

      for (let w = 0; w < chwl; w++) {
        const aa = columeHeader_word[w]

        for (let n = 0; n < chwl; n++) {
          const bqa = bb + aa + columeHeader_word[n]
          ret.push(bqa)
          c++

          if (c > n) {
            return ret
          }
        }
      }
    }
  }
}

/**
 * 计算字符串字节长度
 * @param {*} val 字符串
 * @param {*} subLen 要截取的字符串长度
 */
function getByteLen(val, subLen) {
  if (subLen === 0) {
    return ''
  }

  if (val == null) {
    return 0
  }

  let len = 0
  for (let i = 0; i < val.length; i++) {
    const a = val.charAt(i)

    // eslint-disable-next-line no-control-regex
    if (a.match(/[^\x00-\xFF]/gi) != null) {
      len += 2
    }
    else {
      len += 1
    }

    if (isRealNum(subLen) && len === ~~subLen) {
      return val.substring(0, i)
    }
  }

  return len
}

// 数组去重
function ArrayUnique(dataArr: any[]) {
  const result: any[] = []
  const obj = {}
  if (dataArr.length > 0) {
    for (let i = 0; i < dataArr.length; i++) {
      const item = dataArr[i]
      if (!obj[item]) {
        result.push(item)
        obj[item] = 1
      }
    }
  }
  return result
}

// 获取字体配置
function luckysheetfontformat(format) {
  const fontarray = locale().fontarray
  if (getObjType(format) === 'object') {
    let font = ''

    // font-style
    if (format.it === '0' || format.it == null) {
      font += 'normal '
    }
    else {
      font += 'italic '
    }

    // font-variant
    font += 'normal '

    // font-weight
    if (format.bl === '0' || format.bl == null) {
      font += 'normal '
    }
    else {
      font += 'bold '
    }

    // font-size/line-height
    if (!format.fs) {
      font += `${Store.defaultFontSize}pt `
    }
    else {
      font += `${Math.ceil(format.fs)}pt `
    }

    if (!format.ff) {
      font += `${fontarray[0]}, "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif`
    }
    else {
      let fontfamily: string | null = null
      if (isdatatypemulti(format.ff).num) {
        fontfamily = fontarray[Number.parseInt(format.ff)]
      }
      else {
        // fontfamily = fontarray[fontjson[format.ff]];
        fontfamily = format.ff

        fontfamily = fontfamily?.replace(/"/g, '').replace(/'/g, '') || null

        if (fontfamily?.includes(' ')) {
          fontfamily = `"${fontfamily}"`
        }

        if (
          fontfamily !== null
          && document.fonts
          && !document.fonts.check(`12px ${fontfamily}`)
        ) {
          menuButton.addFontTolist(fontfamily)
        }
      }

      if (fontfamily === null) {
        fontfamily = fontarray[0]
      }

      font += `${fontfamily}, "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Hiragino Sans GB", "Heiti SC", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif`
    }

    return font
  }
  else {
    return luckysheetdefaultFont()
  }
}

// 右键菜单
function showrightclickmenu($menu, x, y) {
  const winH = $(window).height()!
  const winW = $(window).width()!
  const menuW = $menu.width()
  const menuH = $menu.height()
  let top = y
  let left = x

  if (x + menuW > winW) {
    left = x - menuW
  }

  if (y + menuH > winH) {
    top = y - menuH
  }

  if (top < 0) {
    top = 0
  }

  $menu.css({ top, left }).show()
}

// 单元格编辑聚焦
function luckysheetactiveCell() {
  if (Store.fullscreenmode) {
    setTimeout(() => {
      // need preventScroll:true,fix Luckysheet has been set top, and clicking the cell will trigger the scrolling problem
      const input = document.getElementById('luckysheet-rich-text-editor')!
      input.focus({ preventScroll: true })
      $('#luckysheet-rich-text-editor').select()
      // $("#luckysheet-rich-text-editor").focus().select();
    }, 50)
  }
}

// 单元格编辑聚焦
function luckysheetContainerFocus() {
  // $("#" + Store.container).focus({
  //     preventScroll: true
  // });

  // fix jquery error: Uncaught TypeError: ((n.event.special[g.origType] || {}).handle || g.handler).apply is not a function
  // $("#" + Store.container).attr("tabindex", 0).focus();

  // need preventScroll:true,fix Luckysheet has been set top, and clicking the cell will trigger the scrolling problem
  document.getElementById(Store.container!)!.focus({ preventScroll: true })
}

// 数字格式
function numFormat(num, type) {
  if (
    num == null
    || Number.isNaN(Number.parseFloat(num))
    || hasChinaword(num)
    || num === -Infinity
    || num === Infinity
  ) {
    return null
  }

  let floatlen = 6
  let ismustfloat = false
  if (type == null || type === 'auto') {
    if (num < 1) {
      floatlen = 6
    }
    else {
      floatlen = 1
    }
  }
  else {
    if (isdatatype(type) === 'num') {
      floatlen = Number.parseInt(type)
      ismustfloat = true
    }
    else {
      floatlen = 6
    }
  }

  let format = ''
  let value = 0
  for (let i = 0; i < floatlen; i++) {
    format += '0'
  }

  if (!ismustfloat) {
    format = `[${format}]`
  }

  if (num >= 1e21) {
    value = Number.parseFloat(numeral(num).value())
  }
  else {
    value = Number.parseFloat(numeral(num).format(`0.${format}`))
  }

  return value
}

function numfloatlen(n) {
  if (n != null && !Number.isNaN(Number.parseFloat(n)) && !hasChinaword(n)) {
    const value = numeral(n).value()
    let lens = value.toString().split('.')

    if (lens.length === 1) {
      lens = 0
    }
    else {
      lens = lens[1].length
    }

    return lens
  }
  else {
    return null
  }
}

// 二级菜单显示位置
function mouseclickposition($menu, x, y, p) {
  const winH = $(window).height()!
  const menuW = $menu.width()

  if (p == null) {
    p = 'lefttop'
  }

  if (p === 'lefttop') {
    $menu.css({ top: y, left: x }).show()
  }
  else if (p === 'righttop') {
    $menu.css({ top: y, left: x - menuW }).show()
  }
  else if (p === 'leftbottom') {
    $menu.css({ bottom: winH - y - 12, left: x }).show()
  }
  else if (p === 'rightbottom') {
    $menu.css({ bottom: winH - y - 12, left: x - menuW }).show()
  }
}

/**
 * 元素选择器
 * @param {string}  selector css选择器
 * @param {string}  context  指定父级DOM
 */
function $$(selector, context) {
  context = context || document
  const elements = context.querySelectorAll(selector)
  return elements.length === 1
    ? Array.prototype.slice.call(elements)[0]
    : Array.prototype.slice.call(elements)
}

/**
 * 串行加载指定的脚本
 * 串行加载[异步]逐个加载，每个加载完成后加载下一个
 * 全部加载完成后执行回调
 * @param {Array | string}  scripts 指定要加载的脚本
 * @param {object} options 属性设置
 * @param {Function} callback 成功后回调的函数
 * @return {Array} 所有生成的脚本元素对象数组
 */

function seriesLoadScripts(scripts, options, callback) {
  if (typeof scripts !== 'object') {
    scripts = [scripts]
  }
  const HEAD
    = document.getElementsByTagName('head')[0] || document.documentElement
  const s: HTMLScriptElement[] = []
  const last = scripts.length - 1
  // 递归
  const recursiveLoad = function (i) {
    s[i] = document.createElement('script')
    s[i].setAttribute('type', 'text/javascript')
    // Attach handlers for all browsers
    // 异步
    s[i].onload = s[i].onratechange = function () {
      const that = this as any
      if (
        !0
        || that.readyState === 'loaded'
        || that.readyState === 'complete'
      ) {
        this.onload = that.onreadystatechange = null
        that.parentNode.removeChild(this)
        if (i !== last) {
          recursiveLoad(i + 1)
        }
        else if (typeof callback === 'function') {
          callback()
        }
      }
    }
    // 同步
    s[i].setAttribute('src', scripts[i])

    // 设置属性
    if (typeof options === 'object') {
      for (const attr in options) {
        s[i].setAttribute(attr, options[attr])
      }
    }

    HEAD.appendChild(s[i])
  }
  recursiveLoad(0)
}

/**
 * 并行加载指定的脚本
 * 并行加载[同步]同时加载，不管上个是否加载完成，直接加载全部
 * 全部加载完成后执行回调
 * @param {Array | string}  scripts 指定要加载的脚本
 * @param {object} options 属性设置
 * @param {Function} callback 成功后回调的函数
 * @return {Array} 所有生成的脚本元素对象数组
 */

function parallelLoadScripts(scripts: string | string[], options, callback) {
  if (typeof scripts !== 'object') {
    scripts = [scripts]
  }
  const HEAD
    = document.getElementsByTagName('head')[0] || document.documentElement
  const s: HTMLScriptElement[] = []
  let loaded = 0
  for (let i = 0; i < scripts.length; i++) {
    s[i] = document.createElement('script')
    s[i].setAttribute('type', 'text/javascript')
    // Attach handlers for all browsers
    // 异步
    s[i].onload = s[i].onratechange = function () {
      const that = this as any
      if (!0) {
        loaded++
        that.onload = that.onratechange = null
        that.parentNode.removeChild(that)
        if (loaded === scripts.length && typeof callback === 'function')
          callback()
      }
    }
    // 同步
    s[i].setAttribute('src', scripts[i])

    // 设置属性
    if (typeof options === 'object') {
      for (const attr in options) {
        s[i].setAttribute(attr, options[attr])
      }
    }

    HEAD.appendChild(s[i])
  }
}

/**
 * 动态添加css
 * @param {string}  url 指定要加载的css地址
 */
function loadLink(url) {
  const doc = document
  const link = doc.createElement('link')
  link.setAttribute('rel', 'stylesheet')
  link.setAttribute('type', 'text/css')
  link.setAttribute('href', url)

  const heads = doc.getElementsByTagName('head')
  if (heads.length) {
    heads[0].appendChild(link)
  }
  else {
    doc.documentElement.appendChild(link)
  }
}

/**
 * 动态添加一组css
 * @param {string}  url 指定要加载的css地址
 */
function loadLinks(urls) {
  if (typeof urls !== 'object') {
    urls = [urls]
  }
  if (urls.length) {
    urls.forEach((url) => {
      loadLink(url)
    })
  }
}

function transformRangeToAbsolute(txt1) {
  if (txt1 == null || txt1.length === 0) {
    return null
  }

  const txtArray = txt1.split(',')
  let ret = ''
  for (let i = 0; i < txtArray.length; i++) {
    const txt = txtArray[i]
    const txtSplit = txt.split('!')
    let sheetName = ''
    let rangeTxt = ''
    if (txtSplit.length > 1) {
      sheetName = txtSplit[0]
      rangeTxt = txtSplit[1]
    }
    else {
      rangeTxt = txtSplit[0]
    }

    const rangeTxtArray = rangeTxt.split(':')

    let rangeRet = ''
    for (let a = 0; a < rangeTxtArray.length; a++) {
      const t = rangeTxtArray[a]

      const row = t.replace(/\D/g, '')
      const col = t.replace(/[^A-Z]/gi, '')
      let rangeTT = ''
      if (col !== '') {
        rangeTT += `$${col}`
      }

      if (row !== '') {
        rangeTT += `$${row}`
      }

      rangeRet += `${rangeTT}:`
    }

    rangeRet = rangeRet.substr(0, rangeRet.length - 1)

    ret += `${sheetName + rangeRet},`
  }

  return ret.substr(0, ret.length - 1)
}

function openSelfModel(id, isshowMask = true) {
  const $t = $(`#${id}`)
    .find('.luckysheet-modal-dialog-content')
    .css('min-width', 300)
    .end()
  const myh = $t.outerHeight()!
  const myw = $t.outerWidth()!
  const winw = $(window).width()!
  const winh = $(window).height()!
  const scrollLeft = $(document).scrollLeft()!
  const scrollTop = $(document).scrollTop()!
  $t.css({
    left: (winw + scrollLeft - myw) / 2,
    top: (winh + scrollTop - myh) / 3,
  }).show()

  if (isshowMask) {
    $('#luckysheet-modal-dialog-mask').show()
  }
}

/**
 * 监控对象变更
 * @param {*} data
 */
// const createProxy = (data,list=[]) => {
//     if (typeof data === 'object' && data.toString() === '[object Object]') {
//       for (let k in data) {
//         if(list.includes(k)){
//             if (typeof data[k] === 'object') {
//               defineObjectReactive(data, k, data[k])
//             } else {
//               defineBasicReactive(data, k, data[k])
//             }
//         }
//       }
//     }
// }

function createProxy(data, k, callback) {
  // eslint-disable-next-line no-prototype-builtins
  if (!data.hasOwnProperty(k)) {
    // eslint-disable-next-line no-console
    console.info('No %s in data', k)
    return
  }

  if (getObjType(data) === 'object') {
    if (getObjType(data[k]) === 'object' || getObjType(data[k]) === 'array') {
      defineObjectReactive(data, k, data[k], callback)
    }
    else {
      defineBasicReactive(data, k, data[k], callback)
    }
  }
}

function defineObjectReactive(obj, key, value, callback) {
  // 递归
  obj[key] = new Proxy(value, {
    set(target, property, val, receiver) {
      setTimeout(() => {
        callback(target, property, val, receiver)
      }, 0)

      return Reflect.set(target, property, val, receiver)
    },
  })
}

function defineBasicReactive(obj, key, value, callback) {
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: false,
    get() {
      return value
    },
    set(newValue) {
      if (value === newValue)
        return

      setTimeout(() => {
        callback(value, newValue)
      }, 0)

      value = newValue
    },
  })
}

/**
 * Remove an item in the specified array
 * @param {Array} array Target array
 * @param {string} item What needs to be removed
 */
function arrayRemoveItem(array, item) {
  array.some((curr, index, arr) => {
    if (curr === item) {
      arr.splice(index, 1)
      return curr === item
    }
    return false
  })
}

/**
 * camel 形式的单词转换为 - 形式 如 fillColor -> fill-color
 * @param {string} camel camel 形式
 * @returns
 */
function camel2split(camel) {
  return camel.replace(/([A-Z])/g, (all, group) => {
    return `-${group.toLowerCase()}`
  })
}

export {
  $$,
  ABCatNum,
  arrayRemoveItem,
  ArrayUnique,
  camel2split,
  ceateABC,
  chatatABC,
  common_extend,
  createABCdim,
  createProxy,
  getByteLen,
  getNowDateTime,
  getObjType,
  hexToRgb,
  isJsonString,
  loadLinks,
  luckysheetactiveCell,
  luckysheetContainerFocus,
  luckysheetfontformat,
  mouseclickposition,
  numfloatlen,
  numFormat,
  openSelfModel,
  parallelLoadScripts,
  replaceHtml,
  rgbTohex,
  seriesLoadScripts,
  showrightclickmenu,
  transformRangeToAbsolute,
}
